#ifndef __tenacitas_log_core_logger_tester_h__
#define __tenacitas_log_core_logger_tester_h__

#include <iostream>
#include <cstdint>
#include <string>
#include <fstream>

#include "runtest.h"

#include <tenacitas.return_code.core/rc.h>
#include <tenacitas.log.core/logger.h>
#include <tenacitas.translate.core/file_translator.h>


namespace tenacitas {
    namespace log {
        namespace core {

            class logger_tester {

            private:
                struct cerr_media {

                    cerr_media(cerr_media && p_media) {
                        m_thread_id = std::move(p_media.m_thread_id);
                    }

                    cerr_media(const thread_id & p_thread_id)
                        : m_thread_id(p_thread_id){}
                    
                    rc<> new_line(level p_level,
                                  uint64_t p_nano,
                                  const char * p_file,
                                  int64_t p_line) {
                        std::cerr << "\n"
                                  << p_level.str() << "|"
                                  << p_nano << "|"
                                  << m_thread_id << "|"
                                  << p_file << "|"
                                  << p_line;
                        return rc<>();
                    }


                    template <typename t_data>
                    rc<> write(const t_data & p_data) {
                        std::cerr << "|" << p_data;
                        return rc<>();
                    }

                    template <typename t_data>
                    rc<> write( t_data && p_data) {
                        std::cerr << "|" << p_data;
                        return rc<>();
                    }

                    cerr_media & operator=(cerr_media && p_media) {
                        m_thread_id = std::move(p_media.m_thread_id);
                        return *this;
                    }

                private:
                    thread_id m_thread_id;
                };

                
                
                struct supplier_cerr {
                    typedef cerr_media media;
                    typedef core::logger<supplier_cerr> logger;
                };
                
                struct file_media {

                    file_media(const thread_id & p_th_id)
                        : m_th_id(p_th_id),
                          m_file() {
                        std::stringstream l_stream;
                        l_stream.width(3);
                        l_stream.fill('0');
                        
                        l_stream << "/var/tmp/file_media_" << m_th_id << ".log";
                        m_file = std::unique_ptr<std::ofstream>
                            (new std::ofstream(l_stream.str())); 
                    }

                    file_media(file_media && p_media) {
                        m_th_id = std::move(p_media.m_th_id);
                        m_file = std::move(p_media.m_file);
                    }

                    file_media & operator=(file_media && p_media) {
                        m_th_id = std::move(p_media.m_th_id);
                        m_file = std::move(p_media.m_file);
                        return *this;
                    }
                        
                    template <typename t_data>
                    rc<> write(const t_data & p_data) {
                        (*m_file) << "|" << p_data;
                        return rc<>();                        
                    }
                    
                    template <typename t_data>
                    rc<> write( t_data && p_data) {
                        (*m_file) << "|" << p_data;
                        return rc<>();                        
                    }

                    rc<> new_line(level p_level,
                                  uint64_t p_nanosecs,
                                  const char * p_file,
                                  int64_t p_line) {
                        (*m_file) << std::endl
                                  << p_level.str() << "|"
                                  << p_nanosecs << "|"
                                  << m_th_id << "|"
                                  << p_file << "|"
                                  << p_line;
                        return rc<>();
                    }


                    ~file_media() {
                        // if  (m_file->good() && (m_file->is_open()) ) {
                        //     m_file->close();
                        // }
                    }

                private:
                    // static const std::string m_dir_name;
                    // static const std::string m_base_file_name;
                    // static uint16_t m_file_counter;

                    thread_id m_th_id;
                    std::unique_ptr<std::ofstream> m_file;
                };

                // const std::string file_media::m_dir_name = "/var/tmp/";
                // const std::string file_media::m_base_file_name = "t002";
                
                struct supplier_file {
                    typedef file_media media;
                    typedef core::logger<supplier_file> logger;
                };

                static bool test1() {
                    std::cerr << "\n+++ logger_tester::test1" << std::endl;
                    logger<supplier_cerr> _logger;
                    log_error(_logger) << "f1";
                    return true;
                }

                static bool test2() {
                    std::cerr << "\n+++ logger_tester::test2" << std::endl;
                    logger<supplier_cerr> _logger;
                    log_error(_logger) << "another error here";
                    _logger.set_level(level::trace);
                    log_trace(_logger) << "tracing";
                    return true;
                }

                static bool test3() {
                    std::cerr << "\n+++ logger_tester::test3" << std::endl;
                    logger<supplier_cerr> _logger;
                    log_error(_logger) << "another error here";
                    _logger.set_level(level::trace);
                    log_trace(_logger) << "tracing";
                    return true;
                }
                
                static bool test4() {
                    std::cerr << "\n+++ logger_tester::test4" << std::endl;
                    logger<supplier_cerr> _logger;
                    log_error(_logger) << "one more error here";
                    log_trace(_logger) << "tracing again";
                    log_debug(_logger) << "debugging";
                    return true;
                }
                
                static bool test5() {
                    std::cerr << "\n+++ logger_tester::test5" << std::endl;
                    logger<supplier_file> _logger;
                    log_info(_logger) << "ola" << 456.12;
                    return true;                    
                }

                static bool test6() {
                    std::cerr << "\n+++ logger_tester::test6" << std::endl;
                    logger<supplier_cerr> _logger;
                    rc<float> _rc(8765, 104, __FILE__, __LINE__);
                    translate::core::file_translator<supplier_cerr> _translator;
                    std::string 
                        _translation_file(std::string(DIR_TEST) +
                                          std::string("/my_struct_pt-BR.trans"));
                    _translator.add(_translation_file);
                    log_info(_logger) << _translator(_rc);
                    
                    return true;                    
                }            

                static bool test7() {
                    std::cerr << "\n+++ logger_tester::test7" << std::endl;
                    logger<supplier_cerr> _logger;
                    rc<float> _rc(1234, 2, __FILE__, __LINE__);
                    translate::core::file_translator<supplier_cerr> _translator;
                    std::string 
                        _translation_file(std::string(DIR_TEST) +
                                          std::string("/my_struct_pt-BR.trans"));
                    _translator.add(_translation_file);
                    log_info(_logger) << _translator(_rc);
                    
                    return true;                    
                }            

            public:
                static void run() {
                    
                    runtest(logger_tester::test1());
                    runtest(logger_tester::test2());
                    runtest(logger_tester::test3());
                    runtest(logger_tester::test4());
                    runtest(logger_tester::test5());
                    runtest(logger_tester::test6());
                    runtest(logger_tester::test7());
                }

            };

        }
    }
}

#endif
